package ch07 {
	import adobe.utils.CustomActions;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import flash.utils.getTimer;

	/**
	 * 3D旋转
	 * 解决景深排序问题
	 */
	[SWF(width=800,height=800,backgroundColor=0xffffff)]
	public class RotateAndPosition2 extends Sprite {
		private var _holder:Sprite;
		private var _shapes:Array;

		public function RotateAndPosition2(){
			_holder = new Sprite();
			/**
			 * 当显式设置一个显示对象的 z 属性或任何旋转或缩放属性时，
			 * 将自动创建相应的 Matrix3D 对象。
			 * 2D 对象的 z 属性的值为零，并且其 matrix3D 属性的值为 null。
			 */
			_holder.x = stage.stageWidth / 2;
			_holder.y = stage.stageHeight / 2;
			_holder.z = 0;
			addChild(_holder);

			var shape1:Shape = makeShape();
			shape1.z = 200;

			var shape2:Shape = makeShape();
			shape2.z = -200;

			var shape3:Shape = makeShape();
			shape3.x = 200;
			shape3.rotationY = 90;

			var shape4:Shape = makeShape();
			shape4.x = -200;
			shape4.rotationY = -90;

			var shape5:Shape = makeShape();
			shape5.y = 200;
			shape5.rotationX = 90;

			var shape6:Shape = makeShape();
			shape6.y = -200;
			shape6.rotationX = 90;

			shape1.name = "shape1";
			shape2.name = "shape2";
			shape3.name = "shape3";
			shape4.name = "shape4";
			shape5.name = "shape5";
			shape6.name = "shape6";

			_shapes = [shape1, shape2, shape3, shape4, shape5, shape6];
			sortShapes();

			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function makeShape():Shape {
			var shape:Shape = new Shape();
			var g:Graphics = shape.graphics;
			g.beginFill(Math.random() * 0xffffff);
			g.drawRect(-100, -100, 200, 200);
			g.endFill();
			_holder.addChild(shape);
			return shape;
		}

        //方法一:
		private function sortShapes():void {
			//_shapes.sortOn("z");
			/**
			 * 按照Z值顺序对对象数组进行排序，并且按照顺序添加到_holder上，但是，测试发现，
			 * 该方法没有弥补景深错误。问题出在我们只是在容易内部按照Z值排序，
			 * 所以尽管他们有正确的Z值顺序，但当容易旋转的时候，他们刚好突然完全地反转了。
			 * 我们需要做的是从holder的外面来进来正确的排序。换句话说，即使对象A的Z值比对象B小，
			 * 当容易就Z值来说被旋转至反转后，她叶会出现在对象B的后面。
			 *
			 * 为了解决这个问题，按惯例我们要写一个函数把容器内的局部坐标系转换为世界（或舞台、根）坐标系，
			 * 然后根据它进行排序。Array.sort方法允许你把一个函数作为参数传递。
			 * 然后这个函数会随着数组中一对对的对象排序而被多次调用。如果数组中第一个对象应该放置在第二个对象的前面，
			 * 这个函数会返回一个负数；如果数组中第一个对象应该放置在第二个对象的后面，这个函数会返回一个正数；
			 * 如果应该放置在它原来的位置，就返回零。
			 */
			_shapes.sort(depthSort);
            
			//sortShapes2();
			for each (var shape:Shape in _shapes){
				trace(shape.name);
				_holder.addChild(shape);
			}
			trace("++++++++++++++++++++++++++++++++++++++++++");
		}

        //方法二:
		private function sortShapes2():void {
			var num_loop:int = _shapes.length;
			var itemi:Shape;
			var itemj:Shape;
			var tempShape:Shape;
			var zi:int;
			var zj:int;
			for (var i:int = 0; i < num_loop - 1; i++){
				itemi = _shapes[i];
				zi = itemi.transform.getRelativeMatrix3D(root).position.z;
				for (var j:int = i + 1; j < num_loop; j++){
					itemj = _shapes[j];
					zj = itemj.transform.getRelativeMatrix3D(root).position.z;
					if (zj > zi){
						_shapes[i] = itemj;
						_shapes[j] = itemi;
						break;
					}
				}
			}
		}

		private function depthSort(objA:DisplayObject, objB:DisplayObject):int {
			/**
			 * position属性:
			 * 一个保存显示对象在转换参照帧中的 3D 坐标 (x,y,z) 位置的 Vector3D 对象。
			 * 利用 position 属性，可立即访问显示对象矩阵的平移矢量，而无需分解和重新组合矩阵。
			 *
			 * transformVector()方法:
			 * 使用转换矩阵将 Vector3D 对象从一个空间坐标转换到另一个空间坐标。
			 * 返回的 Vector3D 对象将容纳转换后的新坐标。将对 Vector3D 对象中应用所有矩阵转换（包括平移）。
			 *
			 *
			   deltaTransformVector () 方法
			   public function deltaTransformVector(v:Vector3D):Vector3D

			   使用不含平移元素的转换矩阵将 Vector3D 对象从一个空间坐标转换到另一个空间坐标。返回的 Vector3D 对象将容纳应用旋转和缩放转换之后的新坐标。如果 deltaTransformVector() 方法应用的是仅包含平移转换的矩阵，则返回的 Vector3D 对象与原始 Vector3D 对象相同。

			   通过使用 deltaTransformVector() 方法，可以让一个坐标空间中的显示对象对第二个显示对象的旋转转换做出响应。该对象不复制旋转；它只是更改其位置以反映旋转中的更改。例如，为了使用 display.Graphics API 绘制一个旋转的 3D 显示对象，则必须将该对象的旋转坐标映射到一个 2D 点。首先，在每次旋转后使用 deltaTransformVector() 方法检索对象的 3D 坐标。然后，应用显示对象的 local3DToGlobal() 方法，以将 3D 坐标平移到 2D 点。然后，可以使用 2D 点来绘制旋转的 3D 对象
			 */
			var _holderMatrix3D:Matrix3D = _holder.transform.matrix3D;
			var posA:Vector3D = objA.transform.matrix3D.position;
			//trace(posA);
			posA = _holderMatrix3D.deltaTransformVector(posA);
			//trace("after:", posA, "\n");
			var posB:Vector3D = objB.transform.matrix3D.position;
			posB = _holderMatrix3D.deltaTransformVector(posB);

			var posC:Vector3D =objA.transform.getRelativeMatrix3D(root).position;
			//var posD:Vector3D = objB.transform.getRelativeMatrix3D(root).position;
            trace(posA,posC);
			return posB.z - posA.z;
		}

		private function onEnterFrame(event:Event):void {
			_holder.rotationY += .5;
			_holder.rotationX += 2;
			sortShapes();
		}
	}
}
